Document本身有提供一些方法可以存取節點,例如:Document.body
、Document.head
,會各自回傳<body>
跟<head>
內容,比方說使用document.body.style.backgroundColor = "yellow";
會更改背景的顏色。
Selectors API
提供querySelector
比對選擇器,去搜尋DOM中的節點方法。querySelector分成querySelector()
跟querySelectorAll()
。
document.querySelector()
簡單來說在DOM去搜尋節點,括號中需要有單引號或雙引號,內容照平常使用選擇器的方式來寫。看到document.querySelectorAll()
的名字,就會想document.querySelector()
是不是只選某個元素。但更貼近這個方法的說法是,使用這個方法會回傳文件中的第一個元素,即使符合選擇器的元素很多,也只會回傳這個序列中的第一個。
範例程式碼:
<h1>contestantNumber: 1</h1>
<ul>
<li>hotpotFlavor: Spicy Sichuan</li>
<li>hotpotIngredients:Beef slices, Tofu, Enoki mushrooms, Napa cabbage</li>
<li>beveragePairing: Jasmine tea</li>
</ul>
用querySelector()
跟選擇程式碼中的<li>
:
console.log(document.querySelector('li'))
element.querySelector()
跟document不一樣的是,element可以選擇內建的document方法或者document.queryselector
選到的元素。
拿上面的程式碼來示範:
//存到變數
const ul = document.querySelector('ul')
console.log(ul.querySelector('li'))
//也可以不要存變數
console.log(document.querySelector('ul').querySelector('li'))
//使用預設方法
console.log(document.body.querySelector('li'))
有趣的是當我使用codepen來嘗試這些程式碼,我沒有把這些程式碼放到body,但預設上已經把這些元素當成他的child,也可以存取到這些node。(不過我只是測試用,平常請寫完整)
document.querySelectorAll()
當使用這個方法,可以一次選取多個節點,使用時會回傳一個靜態的nodelist。
依照剛剛的程式碼,改成document.querySelectorAll()
:
console.log(document.querySelectorAll('li'))
會回傳一個有序列跟長度的nodelist。
element.querySelectorAll()
用法同理先前element.querySelector()
,回傳的結果也跟document.querySelectorAll()
一樣回傳一個靜態的nodelist。
不像querySelectorAll()
會回傳一個list,在這個方法中ID是唯一的,即使有重複的ID,也只會回傳一個。
例如把每個<li>
加上一個同樣的ID:
<h1>contestantNumber: 1</h1>
<ul>
<li id='text'>hotpotFlavor: Spicy Sichuan</li>
<li id='text'>>hotpotIngredients:Beef slices, Tofu, Enoki mushrooms, Napa cabbage</li>
<li id='text'> beveragePairing: Jasmine tea
</li>
</ul>
console.log(document.getElementById('text'))
僅回傳這一個元素。
使用class name選取,中間可以用一個空格隔開,去找完全符合這個條件的元素。
<h1>contestantNumber: 1</h1>
<ul>
<li class='text'>hotpotFlavor: Spicy Sichuan</li>
<li class='text'>>hotpotIngredients:Beef slices, Tofu, Enoki mushrooms, Napa cabbage</li>
<li class='text'> beveragePairing: Jasmine tea
</li>
</ul>
console.log(document.getElementsByClassName('text'))
會回傳一組Collection可以使用:
修改class name,讓方法去搜尋。現在三個li都有共通的名字叫text
,第一個多兩個名字為one
跟flavor
,第二個則是two
,讓我們在方法中輸入text one
。
<h1>contestantNumber: 1</h1>
<ul>
<li class="text one flavor">hotpotFlavor: Spicy Sichuan</li>
<li class="text two">hotpotIngredients:Beef slices, Tofu, Enoki mushrooms, Napa cabbage</li>
<li class="text">beveragePairing: Jasmine tea</li>
</ul>
console.log(document.getElementsByClassName('text one'))
回傳:
搜尋符合標籤名稱的元素,例如用剛剛的範例搜尋<li>
,就會回傳像上面的HTML collection。
Name則是每個元素中的屬性內容,而且這個方法會回傳一個動態的nodelist。
<h1>contestantNumber: 1</h1>
<ul>
<li name='text'>hotpotFlavor: Spicy Sichuan</li>
<li name='text'>>hotpotIngredients:Beef slices, Tofu, Enoki mushrooms, Napa cabbage</li>
<li name='text'> beveragePairing: Jasmine tea
</li>
</ul>
console.log(document.getElementsByName('text'))
回傳結果:
上面講了這麽多,而且每個方法回傳的內容都不太一樣。先從Nodelist
跟HTMLCollection
來說起。
Nodelist
是Node的集合,雖然很像陣列但並不是,只能使用一些Array
方法。像剛才的querySelectorAll()
與getElementsByName()
都會回傳一組Nodelist,但前者是一個靜態(static
),另一個則是動態的(live
)。
差異在於如果Nodelist是靜態的,那麽透過querySelectorAll()
回傳一個list之後做出對DOM的改變都是不會發生的,至於動態就會照常變更。
HTMLCollection
是元素的集合,並且有兩個實例方法。如果DOM更新的時候,就會即時反應在HTMLCollection
。
儘管因為動態的關係讓HTMLCollection
看起來很方便,但能用的方法實在不多。
再一次使用剛才的範例:
<h1>contestantNumber: 1</h1>
<ul>
<li>hotpotFlavor: Spicy Sichuan</li>
<li>hotpotIngredients:Beef slices, Tofu, Enoki mushrooms, Napa cabbage</li>
<li>beveragePairing: Jasmine tea
</li>
</ul>
//在最一開始取得li的list
const li = document.querySelectorAll('li');
//取外層容器作為等等新增元素的地方
const ul = document.querySelector('ul')
//新增第四個li
const newLi = document.createElement("li");
//對新的li新增文字
newLi.innerText = `extraDish: Crispy fried wontons`;
//在ul裡面新增剛剛的li
ul.appendChild(newLi);
console.log(li)
而畫面中的確有ul元素:
但nodelist的內容始終只有最一開始的三個:
li.forEach((eachLi=>{
eachLi.style.color="blue"
}))
這時候再對每個<li>
改樣式,就會發現根本改不動第四個。
那要怎麽辦呢?如果只想用document.querySelectorAll()
,就需要在更動DOM的時候再一次使用document.querySelectorAll()
。
const ul = document.querySelector('ul')
const newLi = document.createElement("li");
newLi.innerText = `extraDish: Crispy fried wontons`;
ul.appendChild(newLi);
//在這裡抓一次list
const li2 = document.querySelectorAll('li');
li2.forEach((eachLi=>{
eachLi.style.color="blue"
}))
所有文字都成功變色:
至於想使用哪種方式抓取list,可以看個人需求決定。
題外話,在爬找資料時我突然對querySelectorAll()
存取到的nodelist跟childNodes
的list
感到混亂,才想到querySelector
已經是有用選擇器當作篩選條件,只選取相符合的selector,而像childNodes
是去選父層下面所有的節點。
Live vs. static NodeLists and HTMLCollections in vanilla JS
When is NodeList live and when is it static?